package entities;

import items.Fists;
import items.Item;
import main.Map;

import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import blocks.Block;

import shared.Camera;
import shared.Timer;

public class Player extends WorldEntity {
	//Movement
	private int speed;
	private int jumpPower;
	private boolean isJumping;
	
	//Physics
	private int maxFallSpeed;
	private int currentFallSpeed;
	private int fallSpeedIncrement;
	
	//Gameplay
	private int damage;
	private int attackSpeed = 2;
	private int knockBackPower;
	private Timer knockBackTimer;

	
	//Inventory
	private Item activeItem;
	private Inventory inv;
	private Fists basicFists;
	
	//Graphics
	private Image texture;
	private int direction = 0;
	
	public Player(int x, int y, int width, int height) throws SlickException {
		super(x, y, width, height);
		
		speed = 10;
		setHealth(100);
		jumpPower = 15;
		isJumping = false;
		maxFallSpeed = 25;
		currentFallSpeed = 0;
		fallSpeedIncrement = 1;
		damage = 50;
		knockBackPower = 0;
		knockBackTimer = new Timer(0);
		inv = new Inventory(10, 10, 3);
		basicFists = new Fists(0, 0, 0, inv.getSize(), inv.getSize());
		
		//Items
		if(inv.getSelectedQuickbarItem() != null) {
			activeItem = inv.getSelectedQuickbarItem();
		} else {
			activeItem = basicFists;
		}
	}

	@Override
	public void update(GameContainer gc) throws SlickException {
		move(gc);
		if(knockBackTimer.update()) {
			knockBackPower = 0;
		}
		updatePolygon();
		
		//Check for pickup
		pickupItem();
		
		inv.update(gc, getX(), getY());
		
		//Get QuickbarItem
		if(activeItem != inv.getSelectedQuickbarItem()) {
			if(inv.getSelectedQuickbarItem() != null) {
				activeItem = inv.getSelectedQuickbarItem();
			} else {
				activeItem = basicFists;
			}
		}
		
		if(inv.getSelectedItem() == null && gc.getInput().isMouseButtonDown(Input.MOUSE_LEFT_BUTTON))
			activeItem.use(gc, this);
		
		//Check if itemCount = 0
		for(int i = 0; i < inv.getQuickbarSlots(); i++) {
			if(inv.getQuickbarItemByIndex(i) != null) {
				if(inv.getQuickbarItemByIndex(i).getCount() <= 0) {
					inv.setSelectedItem(null);
					inv.setQuickbarItemByIndex(i, null);
					activeItem = basicFists;
				}
			}
		}

		if(gc.getInput().isKeyPressed(Input.KEY_K)) {
			this.getHit(10, 1, 1);			
		}
		
		if(gc.getInput().isKeyPressed(Input.KEY_P)) {
			Map.addNPC(new NPC(500, 0, 50, 50));
		}
	}

	private void pickupItem() {
		for(int i = 0; i < Map.getItemListSize(); i++) {
			if(!Map.getItemByIndex(i).isJustDropped()) {
				if(Map.getItemByIndex(i).getPolygon().intersects(getPolygon())) {
					inv.addItemToInventoryQuickbar(Map.getItemByIndex(i));
					Map.removeItemByIndex(i);
					Map.setItemIndexCounter(Map.getItemIndexCounter() - 1);
				}
			}
		}
	}

	@Override
	public void draw(GameContainer gc, Graphics g) {
		if(texture != null) {
			g.drawImage(texture, getX(), getY());
			
			g.setColor(Color.white);
			g.drawString("HP: " + Integer.toString(getHealth()), gc.getWidth() - 120 - Camera.getShiftX(), gc.getHeight() - 45 - Camera.getShiftY());
			
			g.setColor(Color.red);
			g.fillRect(gc.getWidth() - 120 - Camera.getShiftX(), gc.getHeight() - 25 - Camera.getShiftY(), getHealth(), 15);
			
			inv.draw(g);
		}
	}
	
	private void move(GameContainer gc) {
		if(currentFallSpeed < maxFallSpeed) {
			currentFallSpeed += fallSpeedIncrement;
		}
		
		if(gc.getInput().isKeyPressed(Input.KEY_SPACE) && !isJumping) {
			currentFallSpeed = -jumpPower;
			isJumping = true;
		}
		
		//Fall
		if(!collidedWithBlock(0, currentFallSpeed)) {
			setY(getY() + currentFallSpeed);
			Camera.moveCamera(0, currentFallSpeed);
			
			if(getY() > 2000) {
				this.die();				
			} 
		} else {
			currentFallSpeed = 0;
			isJumping = false;
		}
		
		//Left Right
		if(gc.getInput().isKeyDown(Input.KEY_A) || knockBackPower < 0) {
			if(!collidedWithBlock(-speed, 0)) {
				setX(getX() - speed);
				Camera.moveCamera(-speed, 0);
				
				if(this.direction == 0) {
					this.flipTextureDirection();
					this.direction = 1;
				}
			} else {
				
			}
		} else if(gc.getInput().isKeyDown(Input.KEY_D) || knockBackPower > 0) {
			if(!collidedWithBlock(speed, 0)) {
				setX(getX() + speed);
				Camera.moveCamera(speed, 0);
				
				if(this.direction == 1) {
					this.flipTextureDirection();
					this.direction = 0;
				}
			} else {
				
			}
		}
	}
	
	private void die() {
		this.setHealthPoints(100);
		this.resetPosition();
	}

	private void resetPosition() {
		this.setX(100);
		this.setY(100);
		
		Camera.init(this.getX(), this.getY(), this.getWidth(), this.getHeight());
	}

	private void setHealthPoints(int hp) {
		setHealth(hp);		
		
		if(getHealth() <= 0) {
			this.die();			
		}
	}
	
	public void getHit(int hp, int knockback, float knockbackTime) {
		setHealth(getHealth() - hp);
		execKnockback(knockback, knockbackTime);
		
		if(getHealth() <= 0) {
			this.die();			
		}
	}

	private void execKnockback(int knockback, float time) {
		knockBackPower = knockback;
		knockBackTimer = new Timer(time);
		knockBackTimer.resetTimer();
	}

	private void flipTextureDirection() {
			this.texture = this.texture.getFlippedCopy(true, false);		
	}

	private boolean collidedWithBlock(int polX, int polY) {
		boolean cl = false;
		Block b = Map.getBlock(getX() + polX, getY() + polY);
		if(b != null) {
			cl = true;
		}
		
		b = Map.getBlock(getX() + getWidth() + polX, getY() + polY);
		if(b != null) {
			cl = true;
		}
		
		b = Map.getBlock(getX() + getWidth() + polX, getY() + getHeight() + polY);
		if(b != null) {
			cl = true;
		}
		
		b = Map.getBlock(getX() + polX, getY() + getHeight() + polY);
		if(b != null) {
			cl = true;
		}
		
		return cl;
	}

	public int getSpeed() {
		return speed;
	}

	public void setSpeed(int speed) {
		this.speed = speed;
	}

	public Image getTexture() {
		return texture;
	}

	public void setTexture(String path) throws SlickException {
		this.texture = new Image(path);
	}

	public int getDamage() {
		return damage;
	}

	public void setDamage(int bareDamage) {
		this.damage = bareDamage;
	}

	public int getAttackSpeed() {
		return attackSpeed;
	}

	public void setAttackSpeed(int attackSpeed) {
		this.attackSpeed = attackSpeed;
	}

	public Item getActiveItem() {
		return activeItem;
	}

	public void setActiveItem(Item activeItem) {
		this.activeItem = activeItem;
	}

	public int getDirection() {
		return direction;
	}

	public void setDirection(int direction) {
		this.direction = direction;
	}
}
